#pragma once

#include <queue>
#include <memory>
#include "thread.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Log.hpp"

namespace threadPoolModule
{
    using namespace threadModule;
    using namespace condModule;
    using namespace mutexModule;
    using namespace logModule;
    
    const int defaultThreadNumber = 5;
    
    template<class Type>
    class ThreadPool
    {
        using Self = ThreadPool<Type>;

        void _wakeUpOne()
        {
            _cond.signal();
            LOG(LogLevel::DEBUG) << "唤醒一个线程";
        }

        void _wakeUpAll()
        {
            _cond.broadcast();
            LOG(LogLevel::DEBUG) << "唤醒所有线程";
        }

        ThreadPool(int num = defaultThreadNumber)
            :_num(num)
            ,_isRunning(false)
            ,_sleepNum(0)
        {
            for (int i = 0; i < num; ++i)
            {
                _threads.emplace_back([this](){ handlerTask(); });
                LOG(LogLevel::DEBUG) << "创建一个线程";
            }
        }

        ThreadPool(const Self&) = delete;
        Self& operator=(const Self&) = delete;
        
    public:

        static Self* getInstance()
        {
            if (_pself == nullptr)
            {
                _mutexStatic.lock();
                if (_pself == nullptr)
                {
                    _pself = new Self();
                }
                _mutexStatic.unlock();
            }

            return _pself;
        }

        void start()
        {
            _mutex.lock();
            
            if (_isRunning == true)
            {
                _mutex.unlock();
                return;
            }
            _isRunning = true;

            _mutex.unlock();

            for (auto& thread : _threads)
            {
                thread.start();
            }

            LOG(LogLevel::DEBUG) << "线程池开始运行";
        }

        void stop()
        {
            _mutex.lock();
            
            if (_isRunning == false)
            {
                _mutex.unlock();
                return;
            }
            _isRunning = false;

            _mutex.unlock();
            
            _wakeUpAll();
            LOG(LogLevel::DEBUG) << "线程池停止运行";
        }

        void join()
        {
            for (auto& thread : _threads)
            {
                thread.join();
                LOG(LogLevel::DEBUG) << "线程回收成功";
            }
        }

        void handlerTask()
        {
            while (true)
            {
                Type task;

                _mutex.lock();

                while (_taskq.empty() && _isRunning == true)        // 当任务队列为空并且线程池正在运行时等待
                {
                    ++_sleepNum;
                    _cond.wait(_mutex);
                    --_sleepNum;
                }
                if (_taskq.empty() && _isRunning == false)          // 当任务队列为空并且线程池停止运行时退出
                {
                    LOG(LogLevel::DEBUG) << "任务队列为空 && 线程池停止运行, 线程停止接收任务";
                    _mutex.unlock();
                    break;
                }
                
                task = std::move(_taskq.front());   // 这里一定能得到任务
                _taskq.pop();

                _mutex.unlock();

                LOG(LogLevel::DEBUG) << "线程开始运行任务";
                task();
                LOG(LogLevel::DEBUG) << "线程结束运行任务";
            }
        }

        bool pushTask(const Type& task)
        {
            _mutex.lock();

            if (_isRunning == false)
            {
                LOG(LogLevel::DEBUG) << "线程池已停止运行，不再接收任务";
                _mutex.unlock();
                return false;
            }

            _taskq.push(task);
            if (_sleepNum == _num)              // 当所有线程睡眠时唤醒一个执行任务
            {
                LOG(LogLevel::DEBUG) << "线程池所有线程睡眠，唤醒一个线程";
                _wakeUpOne();
            }

            _mutex.unlock();

            LOG(LogLevel::DEBUG) << "任务接收成功";

            return true;
        }

        ~ThreadPool() = default;

    private:

        std::vector<thread> _threads;
        int _num;

        std::queue<Type> _taskq;

        Cond _cond;
        Mutex _mutex;

        bool _isRunning;
        int _sleepNum;

        static Self* _pself;                // 懒汉单例模式
        static Mutex _mutexStatic;
    };

    template<class Type>
    ThreadPool<Type>* ThreadPool<Type>::_pself = nullptr;

    template<class Type>
    Mutex ThreadPool<Type>::_mutexStatic;
}
